/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package syntax;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 *
 * @author hicham
 */
public final class Repository {

    private static volatile Repository instance = null;
    private HashMap<String, Message> messages;
    private HashMap<String, Transition> transitions;
    private HashMap<String, Location> locations;
    private HashMap<String, Clock> clocks;
    private Location initialLocation;
    private InternalAction internalAction;

    public Repository clone() {
        Repository repository = new Repository();
        repository.initialLocation = initialLocation;
        repository.internalAction = internalAction;
        repository.messages = new HashMap<String, Message>(messages);
        repository.transitions = new HashMap<String, Transition>(transitions);
        repository.locations = new HashMap<String, Location>(locations);
        repository.clocks = new HashMap<String, Clock>(clocks);
        return repository;
    }
    
    private Repository() {
        super();
        messages = new HashMap<String, Message>();
        transitions = new HashMap<String, Transition>();
        locations = new HashMap<String, Location>();
        clocks = new HashMap<String, Clock>();
    }

    public final static Repository getInstance() {
        if (instance == null) {
            instance = new Repository();
            instance.addInternal();
        }
        return instance;
    }

    public Clock addClock(String name, float l_b, float u_b) {
        Clock res = clocks.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Clock(name, l_b, u_b);
            clocks.put(name, res);
            return res;
        }
    }

    public Input addInput(String name) {
        Message res = messages.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Input(name);
            messages.put(name, res);
            return (Input) res;
        }
    }

    public Output addOutput(String name) {
        Message res = messages.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Output(name);
            messages.put(name, res);
            return (Output) res;
        }
    }

    public InternalAction addInternal() {
        internalAction = new InternalAction();
        messages.put(internalAction.getName(), internalAction);
        return internalAction;

    }

    public Location addLocation(String name) {
        Location res = locations.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Location(name);
            locations.put(name, res);
            return res;
        }
    }

    public Transition addTransition(String name) {
        Transition res = transitions.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Transition(name);
            transitions.put(name, res);
            return res;
        }
    }
    
    public Transition addTransition(Transition transition) {
        String name = transition.getName();
        Transition res = transitions.get(name);
        if (res != null) {
            return null;
        } else {
            transitions.put(name, transition);
            return transition;
        }
    }

    
    private void renameObject(NamedObject object, String newName, HashMap set) {
        set.remove(object.getName());
        object.setName(newName);
        set.put(newName, object);    
    }
    
    public void renameTransition(Transition transition, String newName) {
        renameObject(transition, newName, transitions);
    }

    public void renameClock(Clock clock, String newName) {
        renameObject(clock, newName, clocks);
    }
    public void renameMessage(Message message, String newName) {
        renameObject(message, newName, messages);
    }
    public void renameLocation(Location location, String newName) {
        renameObject(location, newName, locations);
    }
    
    
    public void synchronize(NamedObject object, HashMap objects) {
        objects.put(object.getName(), object);
    }

    public void synchronize(Location location) {
        synchronize(location, locations);
    }

    public void synchronize(Message message) {
        synchronize(message, messages);
    }

    public void synchronize(Clock clock) {
        synchronize(clock, clocks);
    }

    public void synchronize(Transition transition) {
        Location location;
        Transition origTrans = transitions.get(transition.getName());
        String name = null;

        if (origTrans == null) {
            //TODO:
        } else {            
            origTrans.setReachable(transition.isReachable());
            
            //Synchronizing source and destination locations
            synchronize(transition.getSrcLocation());
            name = transition.getSrcLocation().getName();
            origTrans.getSrcLocation().getOutgoingTrans().remove(origTrans);
            location = locations.get(name);
            origTrans.setSrcLocation(location);
            location.getOutgoingTrans().add(origTrans);
            //////////////
            synchronize(transition.getDestLocation());
            name = transition.getDestLocation().getName();
            origTrans.getDestLocation().getIngoingTrans().remove(origTrans);
            location = locations.get(name);
            origTrans.setDestLocation(location);
            location.getIngoingTrans().add(origTrans);

            //Synchronizing message
            synchronize(transition.getMessage());
            name = transition.getMessage().getName();
            origTrans.setMessage(messages.get(name));
            
            //Synchronizing Reset Clocks
            for(Clock clock : transition.getResetClocks()) {
                synchronize(clock);
            }
            origTrans.setResetClocks(transition.getResetClocks());
            
            //Synchronizing Clock Guard
            for(ClockConditionAtom atom : transition.getClockGuard().getConditionAtom()) {
                synchronize(atom.getClock());
            }
            origTrans.setClockGuard(transition.getClockGuard());
        }
    }

    public Transition addTransition(String name, Location src, Location dest) {
        Transition res = transitions.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Transition(name);
            transitions.put(name, res);
            res.setSrcLocation(src);
            res.setDestLocation(dest);
            src.getOutgoingTrans().add(res);
            dest.getIngoingTrans().add(res);
            return res;
        }
    }

    public Transition addTransition(String name, Location src, Location dest, Message message, Collection<Clock> resetClocks, ClockGuard clockGuard) {
        Transition res = transitions.get(name);
        if (res != null) {
            return null;
        } else {
            res = new Transition(name);
            transitions.put(name, res);
            res.setSrcLocation(src);
            res.setDestLocation(dest);
            src.getOutgoingTrans().add(res);
            dest.getIngoingTrans().add(res);
            res.setClockGuard(clockGuard);
            res.setMessage(message);
            for(Clock clock : resetClocks) {
                res.addResetClock(clock);
            }
            return res;
        }
    }
    
    public Message getMessage(String name) {
        return messages.get(name);
    }

    public Clock getClock(String name) {
        return clocks.get(name);
    }

    public Location getLocation(String name) {
        return locations.get(name);
    }

    public Transition getTransition(String name) {
        return transitions.get(name);
    }

    public String printClocks() {
        StringBuilder out = new StringBuilder();
        for (Map.Entry<String, Clock> entry : clocks.entrySet()) {
            out.append("Key = ").append(entry.getKey()).append(", Value = ").append(entry.getValue()).append("\n");
        }
        return out.toString();
    }

    public String printMessages() {
        StringBuilder out = new StringBuilder();
        for (Map.Entry<String, Message> entry : messages.entrySet()) {
            out.append("Key = ").append(entry.getKey()).append(", Value = ").append(entry.getValue()).append("\n");
        }
        return out.toString();
    }

    public String printLocations() {
        StringBuilder out = new StringBuilder();
        for (Map.Entry<String, Location> entry : locations.entrySet()) {
            out.append("Key = ").append(entry.getKey()).append(", Value = ").append(entry.getValue()).append("\n");
        }
        return out.toString();
    }

    public String printTransitions() {
        StringBuilder out = new StringBuilder();
        for (Map.Entry<String, Transition> entry : transitions.entrySet()) {
            out.append("Key = ").append(entry.getKey()).append(", Value = ").append(entry.getValue()).append("\n");
        }
        return out.toString();
    }

    public Collection<Clock> getAllClocks() {
        return clocks.values();
    }

    public Collection<Transition> getAllTransitions() {
        return transitions.values();
    }

    public Collection<Message> getAllMessages() {
        return messages.values();
    }

    public Collection<Location> getAllLocations() {
        return locations.values();
    }

    public String getAllClocksToString() {
        return getAllCollectionToString(getAllClocks());
    }

    public String getAllTransitionsToString() {
        return getAllCollectionToString(getAllTransitions());
    }

    public String getAllMessagesToString() {
        return getAllCollectionToString(getAllMessages());
    }

    public String getAllLocationsToString() {
        return getAllCollectionToString(getAllLocations());
    }

    private String getAllCollectionToString(Collection c) {
        Iterator iteratorClock;
        StringBuilder bf = new StringBuilder();
        if (!c.isEmpty()) {
            iteratorClock = c.iterator();
            bf.append(iteratorClock.next().toString());
            while (iteratorClock.hasNext()) {
                bf.append(",");
                bf.append(iteratorClock.next().toString());
            }
        }
        return bf.toString();
    }

    public void clean() {
        messages.clear();
        transitions.clear();
        locations.clear();
        clocks.clear();
    }

    public InternalAction getInternalAction() {
        return internalAction;
    }
    
    public String print() {
        StringBuilder stb = new StringBuilder();
        stb.append(printClocks());
        stb.append("\n******\n");
        stb.append(printLocations());
        stb.append("\n******\n");
        stb.append(printMessages());
        stb.append("\n******\n");
        stb.append(printTransitions());
        stb.append("\n******\n");
        return stb.toString();
    }
}
